package org.ns.summer.data.mybatis;

import com.atomikos.jdbc.AtomikosDataSourceBean;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.ns.summer.data.jta.JtaTransaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 只管理mybatis
 *
 */
public class MybatisTransactionManage implements AutoCloseable{
    private final static ThreadLocal<MybatisTransactionManage> threadLocal = new ThreadLocal<>();
    private static Logger log = LoggerFactory.getLogger(MybatisTransactionManage.class);
    private long id;
    private Method method; // 执行的方法
    private ExecutorType executorType;
    private JtaTransaction jtaTransaction;
    private boolean autoJta = true; // 发现jta数据源自动开启
    private MybatisTransactionManage() {}

    private Map<String, SqlSession> sqlSessionMap = new HashMap<>();

    public static MybatisTransactionManage create() {
        return create(null, ExecutorType.SIMPLE, true);
    }
    public static MybatisTransactionManage create( boolean autoJta) {
        return  create(null, ExecutorType.SIMPLE, autoJta);
    }
    public static MybatisTransactionManage create(ExecutorType executorType) {
        return  create(null, executorType, true);
    }
    public static MybatisTransactionManage create(ExecutorType executorType, boolean autoJta) {
        return  create(null, executorType, autoJta);
    }
    public static MybatisTransactionManage create(Method method, ExecutorType executorType, boolean autoJta) {
        MybatisTransactionManage manage = threadLocal.get();
        if (manage == null) {
            manage = new MybatisTransactionManage();
            manage.method = method;
            manage.id = Thread.currentThread().getId();
            manage.executorType = executorType;
            manage.autoJta = true;
            threadLocal.set(manage);
            if (log.isDebugEnabled()) {
                if (method != null) {
                    log.debug(String.format("开启Mybatis 线程事务管理！, 启动位置：%s.%s, ", method.getDeclaringClass().getName(), method.getName()));
                } else {
                    log.debug(String.format("开启行内 Mybatis 线程事务管理"));
                }
            }

        }

        return manage;
    }

    public static MybatisTransactionManage get() {
        return threadLocal.get();
    }

    public MybatisTransactionManage JTA() {
        try {
            if (jtaTransaction == null) {
                jtaTransaction = new JtaTransaction();
                jtaTransaction.begin();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return this;
    }

    private void begin() throws Exception{
        if (jtaTransaction != null) {
            jtaTransaction.begin();
        }
    }

    // 获得一个 sqlsession
    public SqlSession openSession(SqlSessionFactory sessionFactory) throws Exception {
        String name = sessionFactory.getConfiguration().getEnvironment().getId();
        SqlSession sqlSession = sqlSessionMap.get(name);
        if (sqlSession == null) {
            if (autoJta && sessionFactory.getConfiguration().getEnvironment().getDataSource() instanceof AtomikosDataSourceBean) {
                JTA();
            }
            if (executorType == null) {
                sqlSession = sessionFactory.openSession(false);
            } else {
                sqlSession = sessionFactory.openSession(executorType);
            }

            sqlSessionMap.put(name, sqlSession);
        }
        begin();
        return sqlSession;
    }

    // commit 提交所有

    public Result commit(Method method) {
        if (method != this.method) {
            return null;
        }
        return commit();
    }

    public Result commit() {
        Result result = new Result();
        Set<String> jtaDataSources = new HashSet<>();
        for (Map.Entry<String, SqlSession> sessionEntry : this.sqlSessionMap.entrySet()) {
            String name = sessionEntry.getKey();
            SqlSession session = sessionEntry.getValue();
            if (jtaTransaction != null && jtaTransaction.isJta(session)) {
                jtaDataSources.add(name);
                continue;
            }
            try {
                log.debug("提交事务: " + name);
                session.commit(true);
            } catch (Exception e) {
                sessionEntry.getValue().rollback();
                rollback(name, session);

            }
        }
        if (jtaTransaction != null && jtaDataSources.size() > 0) {
            try {
                log.debug("提交事务: " + jtaDataSources);
                jtaTransaction.commit();
            } catch (Exception e) {
                result.addFailDataSources(jtaDataSources);
                log.error("jta 事务提交发生异常：" + jtaDataSources , e);
                jtaTransaction.rollbackNoThrows();
            }
        }
        return result;
    }

    private void rollback(String name, SqlSession sqlSession) {
        try {
            sqlSession.rollback(true);
        } catch (Exception e) {
            log.error("回滚异常:" + name , e);
        }
    }
    public void rollback(Method method) {
        if (this.method != method) {
            return;
        }
        rollback();
    }

    public void rollback() {
        Set<String> jtaDataSources = new HashSet<>();
        for (Map.Entry<String, SqlSession> sessionEntry : this.sqlSessionMap.entrySet()) {
            String name = sessionEntry.getKey();
            SqlSession session = sessionEntry.getValue();
            if (jtaTransaction != null && jtaTransaction.isJta(session)) {
                jtaDataSources.add(name);
                continue;
            }
            try {
                session.rollback(true);
            } catch (Exception e) {
                log.error("事务回滚发生异常：" + name , e);
            }
        }

        if (jtaTransaction != null && jtaDataSources.size() > 0) {
            try {
                if(jtaTransaction.isBegin()) {
                    jtaTransaction.rollback();
                }
            } catch (Exception e) {
                log.error("jta 事务回滚发生异常：" + jtaDataSources , e);
            }
        }
    }

    public void close(Method method) {
        if (method == this.method) {
            if (log.isDebugEnabled()) {
                if (method != null) {
                    log.debug(String.format("关闭事务！！%s.%s", method.getDeclaringClass().getName(), method.getName()));
                } else {
                    log.debug("关闭行内事务！！");
                }
            }
            threadLocal.remove();
        }
    }

    public void close() {
        close(null);
    }

    public Method getMethod() {
        return method;
    }

    public long getId() {
        return id;
    }
}
